package cron

import (
	"bigdevops/src/common"
	"bigdevops/src/models"
	"context"
	"encoding/json"
	"fmt"
	"github.com/gammazero/workerpool"
	"go.uber.org/zap"
	"k8s.io/apimachinery/pkg/util/wait"
	"strings"
	"time"
)

// wait.UntilWithContext k8s 基础库 每隔多长时间去执行一次任务
func (cm *CronManager) AutoOrderManager(ctx context.Context) error {
	// 每隔 多长时间去执行一下 RunSyncCloudResource ，直到 ctx.Done
	go wait.UntilWithContext(ctx, cm.RunAutoOrder, time.Duration(cm.Sc.WorkOrderAutoActionC.RunIntervalSeconds)*time.Second)
	<-ctx.Done()
	cm.Sc.Logger.Info("AutoOrderManager 收到其他任务退出信号 退出")
	return nil

}

func (cm *CronManager) RunAutoOrder(ctx context.Context) {
	start := time.Now()
	serviceAccountName := cm.Sc.WorkOrderAutoActionC.ServiceAccount
	if serviceAccountName == "" {
		cm.Sc.Logger.Error("[工单自动执行]自动执行账号没有配置，退出工单执行")
		return
	}
	// 首先要拿到对应的工单
	// 01 根据配置文件拿到 服务账号
	// 再去查询
	//dbUser, err := models.GetUserByUserName(serviceAccountName)
	//if err != nil {
	//	cm.Sc.Logger.Error("[工单自动执行]通过token解析到的userName去数据库中找User失败",
	//		zap.Error(err),
	//	)
	//	return
	//}

	currentFlowNode := fmt.Sprintf("用户@%v", serviceAccountName)
	pendingOrders, count, err := models.GetWorkOrderInstanceByStatusAndCurrentFlowNode(common.WorkOrderInstanceStatusPendingAction, currentFlowNode)
	if err != nil {
		cm.Sc.Logger.Error("[工单自动执行]扫描数据库中待执行的工单失败",
			zap.Error(err),
		)
		return
	}
	cm.Sc.Logger.Info("[工单自动执行]扫描数据库中待执行的总数",
		zap.Int64("总数", count),
	)

	wp := workerpool.New(cm.Sc.WorkOrderAutoActionC.BatchNum)
	for _, pendingOrder := range pendingOrders {
		pendingOrder := pendingOrder
		wp.Submit(func() {
			// 启动任务
			cm.RunAutoOrderOne(pendingOrder)
		})
	}
	wp.StopWait()
	tookSeconds := time.Since(start).Seconds()
	cm.Sc.Logger.Info("[工单自动执行]扫描数据库中待执行的耗时",
		zap.Float64("秒数", tookSeconds),
		zap.Int64("总数", count),
	)
}

func (cm *CronManager) RunAutoOrderOne(pendingOrder *models.WorkOrderInstance) {
	cm.Sc.Logger.Info("[工单自动执行]单条打印",
		zap.Any("name", pendingOrder.Name),
	)
	// 写死
	pendingOrder.FillFrontAllData()
	template := pendingOrder.Template
	if template == nil {
		return
	}
	switch template.Name {
	case cm.Sc.WorkOrderAutoActionC.AutoTemplateNameBuyEcs:
		cm.RunAutoOrderOneBuyEcs(pendingOrder)
	}

}

func (cm *CronManager) RunAutoOrderOneBuyEcs(pendingOrder *models.WorkOrderInstance) {
	cm.Sc.Logger.Info("[工单自动执行-购买虚拟机]完整数据",
		zap.Any("工单名称", pendingOrder),
	)
	var ecsBuyWorkOrderReq models.EcsBuyWorkOrder
	err := json.Unmarshal([]byte(pendingOrder.ActualApiJsonData), &ecsBuyWorkOrderReq)
	if err != nil {
		cm.Sc.Logger.Error("[工单自动执行-购买虚拟机]json解析用户api数据失败",
			zap.Any("工单数据", pendingOrder.ActualApiJsonData),
			zap.Any("工单名称", pendingOrder.Name),
			zap.Error(err),
		)
		return
	}

	// objOne := MockEcsDescribeEcsOne(fmt.Sprintf("固定主机名-%v", i), fmt.Sprintf("fix-id-%v", i), i ,false)
	hostNames := strings.Split(ecsBuyWorkOrderReq.Hostnames, "\n")

	// 先 去db中根据id找到这个对象
	dbNode, err := models.GetStreeNodeById(ecsBuyWorkOrderReq.BindLeafNodeId)
	if err != nil {
		cm.Sc.Logger.Error("[工单自动执行-购买虚拟机]根据id找树节点错误", zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId), zap.Error(err))
		return
	}

	success := true
	successNum := 0
	failNum := 0
	successHosts := []string{}
	failedHosts := []string{}
	errs := []error{}
	for index, hostName := range hostNames {
		objOne := MockEcsDescribeEcsOne(hostName, fmt.Sprintf("uuid-%v", hostName), index, true)
		err = objOne.CreateOne()
		if err != nil {
			cm.Sc.Logger.Error("[工单自动执行-购买虚拟机]创建虚拟机错误",
				zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
				zap.Any("机器名称", hostName),
				zap.Any("工单名称", pendingOrder.Name),
				zap.Error(err))
			errs = append(errs, err)
			success = false
			failNum++
			failedHosts = append(failedHosts, fmt.Sprintf("name:%v 错误:%v\n", hostName, err))
			continue
		}

		// 进行bind
		objOne.BindNodes = append(objOne.BindNodes, dbNode)
		err = objOne.UpdateBindNodes(objOne.BindNodes)
		if err != nil {
			success = false
			failNum++
			failedHosts = append(failedHosts, fmt.Sprintf("name:%v 错误:%v\n", hostName, err))
			cm.Sc.Logger.Error("[工单自动执行-购买虚拟机]虚拟机更新节点错误",
				zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
				zap.Any("机器名称", hostName),
				zap.Any("工单名称", pendingOrder.Name),
				zap.Error(err))
			continue
		}
		cm.Sc.Logger.Info("[工单自动执行-购买虚拟机]虚拟机更新节点成功",
			zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
			zap.Any("机器名称", hostName),
			zap.Any("工单名称", pendingOrder.Name),
		)
		successNum++
		successHosts = append(successHosts, hostName)
	}
	outPut := fmt.Sprintf("总数:%v 成功:%v 失败%v\n"+
		"成功列表:%v\n"+
		"失败详情:%v",
		len(hostNames),
		successNum,
		failNum,
		strings.Join(successHosts, ","),
		strings.Join(failedHosts, ","),
	)

	pendingOrder.Status = common.WorkOrderInstanceStatusFinished

	// 处理时间轴
	var actualFlowData []models.FlowNode
	err = json.Unmarshal([]byte(pendingOrder.ActualFlowData), &actualFlowData)
	if err != nil {
		cm.Sc.Logger.Error("[工单自动执行-购买虚拟机]解析时间轴数据json错误",
			zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
			zap.Any("工单名称", pendingOrder.Name),
			zap.Error(err))
		return
	}

	// 此时认为 执行节点就是最后一个
	index := len(actualFlowData) - 1
	node := actualFlowData[index]
	node.EndTime = time.Now().Format("2006-01-02 15:04:05")
	node.IsPassOrIsSuccess = success
	node.OutPut = outPut
	// 有可能是这一步的问题
	actualFlowData[index] = node
	flowNodeStr, _ := json.Marshal(actualFlowData)
	pendingOrder.ActualFlowData = string(flowNodeStr)
	err = pendingOrder.UpdateOne()
	if err != nil {
		cm.Sc.Logger.Error("[工单自动执行]更新执行记录错误", zap.Any("工单实例", pendingOrder.Name), zap.Error(err))
		return
	}

	cm.Sc.Logger.Info("[工单自动执行-购买虚拟机]更新工单状态成功",
		zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
		zap.Any("工单名称", pendingOrder.Name),
	)

}
